package cn.bitkit.base.util;

import java.lang.reflect.AnnotatedType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import lombok.extern.slf4j.Slf4j;

/**
 * 	
 * @author changbo
 *
 */
@Slf4j
@SuppressWarnings("unchecked")
public class ReflectUtil {
	
	public static <T> T newInstance(Class<T> clazz){
		return newInstance(clazz, null);
	}
	
	public static <T> T newInstance(Class<T> clazz, Object[] parameters){
		try{
			Constructor<T> cons = clazz.getConstructor();
			if(parameters != null){
				return cons.newInstance(parameters);
			}else{
				return cons.newInstance();
			}
		} catch(Exception e){
			log.error("", e);
		}
		return null;
	}

	public static void setValue(Object obj, String field, Object value){
		List<Field> list = getFields(obj.getClass());
		for(Field f : list) {
			if(field.equals(f.getName())) {
				setValue(obj, f, value);
				break;
			}
		}
	}

	public static <T> T getValue(Object obj, String field){
		List<Field> list = getFields(obj.getClass());
		for(Field f : list) {
			if(!field.equals(f.getName())) {
				continue;
			}
			return getValue(obj, f);
		}
		return null;
	}

	public static <T> T getValue(Object obj, Field field){
		try {
			field.setAccessible(true);
			return (T) field.get(obj);
		} catch (Exception e) {
			log.error("", e);
		}
		return null;
	}

	public static void setValue(Object obj, Field field, Object value){
		try {
			field.setAccessible(true);
			field.set(obj, value);
		} catch (Exception e) {
			log.error("", e);
		}
	}
	
	public static <T> T invoke(Object obj, String method) {
		return invoke(obj, method, new Object[] {});
	}
	
	public static <T> T invoke(Object obj, String method, Object... params){
		try {
			Method m = getMethod(obj.getClass(), method);
			m.setAccessible(true);
			return (T) m.invoke(obj, params);
		} catch(Exception e){
			log.error("", e);
		}
		return null;
	}
	
	public static List<Method> getMethods(Class<?> clazz){
        List<Method> list = new ArrayList<>();
        Class<?> superClass = clazz;
        while(superClass != null) {
        	Method[] superMethods = superClass.getDeclaredMethods();
            list.addAll(Arrays.asList(superMethods));
            superClass = superClass.getSuperclass();
        }
        list = list.stream()
        		.filter(e -> !Modifier.isStatic(e.getModifiers()) 
        				&& !Modifier.isFinal(e.getModifiers()))
        		.collect(Collectors.toList());
        return list;
	}
	
	public static Method getMethod(Class<?> clazz, String method) {
        return getMethods(clazz).stream()
				.filter(e -> e.getName().equals(method))
				.findFirst().orElse(null);
	}
	
	public static Class<?> getParameter(Method method, int index) {
		if(method == null) {
			return null;
		}
		Type[] types = method.getGenericParameterTypes();
		if(types.length == 0) {
			return null;
		}
		return (Class<?>) types[index];
	}
	
	public static Class<?> getMethodParameter(Class<?> clazz, String method, int index) {
		Method m = getMethod(clazz, method);
		return getParameter(m, index);
	}

	public static List<Field> getStaticFields(Class<?> clazz){
		List<Field> list = getAllFields(clazz);
		list = list.stream()
				.filter(e -> Modifier.isStatic(e.getModifiers()))
				.collect(Collectors.toList());
		return list;
	}

	public static List<Field> getAllFields(Class<?> clazz){
		List<Field> list = new ArrayList<>();
		Class<?> superClass = clazz;
		while(superClass != null) {
			Field[] superFields = superClass.getDeclaredFields();
			list.addAll(Arrays.asList(superFields));
			superClass = superClass.getSuperclass();
		}
		return list;
	}
	
	public static List<Field> getFields(Class<?> clazz){
        List<Field> list = getAllFields(clazz);
        list = list.stream()
        		.filter(e -> !Modifier.isStatic(e.getModifiers()))
        		.collect(Collectors.toList());
        return list;
	}
	
	public static List<String> getFieldNames(Class<?> clazz){
        return getFields(clazz).stream().map(Field::getName).collect(Collectors.toList());
	}
	
	public static Field getField(Class<?> clazz, String field){
		return getFields(clazz).stream()
				.filter(e -> e.getName().equals(field))
				.findFirst().orElse(null);
	}
	
	public static Class<?> getFieldClass(Class<?> clazz, String field){
		Field f = getField(clazz, field);
		if(f == null) {
			return null;
		}
		return f.getType();
	}
	
	public static Class<?> getActualType(Class<?> clazz){
		return getActualType(clazz, 0);
	}
	
	public static Class<?> getActualType(Class<?> clazz, int index){
		//获取父类
		Type type = clazz.getGenericSuperclass();
		Class<?> ac = getActualType(type, index);
		if(ac != null){
			return ac;
		}
		//如果父类找不到,则找接口
		AnnotatedType[] superInterfaceType = clazz.getAnnotatedInterfaces();
        //遍历每个接口
        for(AnnotatedType at : superInterfaceType) {
            Class<?> ac2 = getActualType(at.getType(), index);
            if(ac2 != null){
                return ac2;
            }
        }
        return null;
	}

	public static Class<?> getActualType(Type type){
		return getActualType(type, 0);
	}

	public static Class<?> getActualType(Type type, int index){
		if(type instanceof ParameterizedType parameterType){
			Type[] types = parameterType.getActualTypeArguments();
			if(types.length > index) {
				return (Class<?>)(types[index]);
			}
		}
		return null;
	}
}
